package com.poly.oss.service;

import cn.hutool.core.io.FastByteArrayOutputStream;
import com.poly.common.core.tools.utils.Func;
import com.poly.common.core.utils.AssertUtil;
import com.poly.common.core.utils.DateTimeUtil;
import com.poly.oss.config.MinioConfig;
import com.poly.oss.model.FileInfoModel;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Stream;

@Component
@RequiredArgsConstructor
public class EndpointService {
    final MinioConfig prop;

    final MinioClient minioClient;

    /**
     * 查看存储bucket是否存在
     *
     * @return boolean
     */
    public Boolean bucketExists(String bucketName) {
        Boolean found;
        try {
            found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return found;
    }

    /**
     * 创建存储bucket
     *
     * @return Boolean
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 删除存储bucket
     *
     * @return Boolean
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 获取全部bucket
     */
    public List<Bucket> getAllBuckets() {
        try {
            List<Bucket> buckets = minioClient.listBuckets();
            return buckets;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 文件上传
     *
     * @param file 文件
     * @return Boolean
     */
    public FileInfoModel upload(MultipartFile file) {
        FileInfoModel fileInfoModel = null;
        try {
            String originalFilename = file.getOriginalFilename();
            AssertUtil.isFalse(file.isEmpty(), "请选择你要上传的文件");
            long size = file.getSize();
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            String trustSuffix = prop.getSuffix();
            if (!Arrays.asList(trustSuffix.split("\\,")).contains(suffix)) {
                throw new IllegalArgumentException("不支持的文件格式类型，支持格式【" + trustSuffix + "】。");
            }
            String fileName = UUID.randomUUID() + suffix;
            String objectName;
            if (Func.isBlank(prop.getPath())) {
                objectName = DateTimeUtil.now("yyyyMMdd") + "/" + fileName;
            } else {
                objectName = prop.getPath() + "/" + DateTimeUtil.now("yyyyMMdd") + "/" + fileName;
            }
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(prop.getBucketName()).object(objectName).stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
            fileInfoModel = new FileInfoModel();
            fileInfoModel.setOriginalFilename(originalFilename);
            fileInfoModel.setSize(size);
            fileInfoModel.setContentType(file.getContentType());
            fileInfoModel.setPutTime(new Date());
            fileInfoModel.setBucketName(prop.getBucketName());
            fileInfoModel.setName(prop.getBucketName().concat("/").concat(objectName));
            fileInfoModel.setSuffix(suffix);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return fileInfoModel;
    }
    /**
     * 流上传
     * */
    public FileInfoModel uploadInside(InputStream inputStream,Long objSize,String fileName,String contentType) {
        FileInfoModel fileInfoModel = null;
        try {
            String objectName;
            if (Func.isBlank(prop.getPath())) {
                objectName = DateTimeUtil.now("yyyyMMdd") + "/" + fileName;
            } else {
                objectName = prop.getPath() + "/" + DateTimeUtil.now("yyyyMMdd") + "/" + fileName;
            }
            int available = inputStream.available();
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(prop.getBucketName()).object(objectName).stream(inputStream, objSize, -1).contentType(contentType).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
            fileInfoModel = new FileInfoModel();
            fileInfoModel.setOriginalFilename(fileName);
            fileInfoModel.setSize((long) available);
            fileInfoModel.setContentType("image/png");
            fileInfoModel.setPutTime(new Date());
            fileInfoModel.setBucketName(prop.getBucketName());
            fileInfoModel.setName(prop.getBucketName().concat("/").concat(objectName));
            fileInfoModel.setSuffix("png");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return fileInfoModel;
    }

    public FileInfoModel statFile(String fileName) {
        return statFile(prop.getBucketName(), fileName);
    }

    public FileInfoModel statFile(String bucketName, String fileName) {
        FileInfoModel fileInfoModel = null;
        try {
            StatObjectResponse stat = minioClient.statObject(
                    StatObjectArgs.builder().bucket(bucketName).object(fileName).build()
            );
            fileInfoModel = new FileInfoModel();
            fileInfoModel.setName(Func.isEmpty(stat.object()) ? fileName : stat.object());
            fileInfoModel.setHash(String.valueOf(stat.hashCode()));
            fileInfoModel.setSize(stat.size());
            fileInfoModel.setPutTime(DateTimeUtil.localDateTimeToDate(stat.lastModified().toLocalDateTime()));
            fileInfoModel.setContentType(stat.contentType());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return fileInfoModel;
    }


    /**
     * 预览
     *
     * @param fileName
     * @return
     */
    public String preview(String fileName) {
        // 查看文件地址
        GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs.builder().bucket(prop.getBucketName()).object(fileName).method(Method.GET).build();
        try {
            String url = minioClient.getPresignedObjectUrl(build);
            return url;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 文件下载
     *
     * @param bucketName
     * @param filename
     * @param originalFilename
     * @param res
     */
    public void download(String bucketName, String filename, String originalFilename, HttpServletResponse res) {
        if (Func.isNotEmpty(filename)) {
            GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName)
                    .object(filename).build();
            try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
                byte[] buf = new byte[1024];
                int len;
                try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
                    while ((len = response.read(buf)) != -1) {
                        os.write(buf, 0, len);
                    }
                    os.flush();
                    byte[] bytes = os.toByteArray();
                    res.setCharacterEncoding("utf-8");
                    // 设置强制下载不打开
                    res.setContentType("application/force-download");
                    res.addHeader("Content-Disposition", "attachment;fileName=" + originalFilename);
                    try (ServletOutputStream stream = res.getOutputStream()) {
                        stream.write(bytes);
                        stream.flush();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            res.setStatus(HttpServletResponse.SC_NOT_FOUND);
        }
    }

    /**
     * 查看文件对象
     *
     * @return 存储bucket内文件对象信息
     */
    public List<Item> listObjects() {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(prop.getBucketName()).build());
        List<Item> items = new ArrayList<>();
        try {
            for (Result<Item> result : results) {
                items.add(result.get());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return items;
    }

    /**
     * 删除
     *
     * @param fileName
     * @return
     * @throws Exception
     */
    public boolean remove(String fileName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(prop.getBucketName()).object(fileName).build());
        } catch (Exception e) {
            return false;
        }
        return true;
    }


    public boolean removeBatch(List<String> fileNames) {
        try {
            Stream<DeleteObject> stream = fileNames.stream().map(DeleteObject::new);
            minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(prop.getBucketName()).objects(stream::iterator).build());
        } catch (Exception e) {
            return false;
        }
        return true;
    }
}
